#ifndef _DATA_QUEUE_
#define _DATA_QUEUE_
// @owner ewolodzko

#include "../Error.h"
#include <pthread.h>
#include <semaphore.h>

template <typename T>
class InputQueue {
	public : virtual const T get(void) = 0;
	virtual ~InputQueue()	{}
};

template <typename T>
class OutputQueue {
	public : virtual void put(const T& data) = 0;
	virtual ~OutputQueue()	{}
};

template <typename T>
class DataQueue : public InputQueue<T>, public OutputQueue<T> {

public:

	DataQueue(int);

	virtual void put(const T& data);
	virtual const T get();
	
	virtual ~DataQueue();

private:

	T* data_array;
	const int size;

	int first, last;
	sem_t read_semaphore, write_semaphore; 
	pthread_mutex_t read_lock, write_lock;

	// Suppress copying.
	DataQueue(const DataQueue&);
	DataQueue& operator=(const DataQueue&)	{	return *this;	}

};

template <typename T>
DataQueue<T>::DataQueue(int Size)
: data_array(NULL)
, size(Size)
, first(0)
, last(0)
, read_semaphore()
, write_semaphore()
, read_lock()
, write_lock()
{
	data_array = new T[size];
	CHECK(sem_init(&read_semaphore, false, 0));
	CHECK(sem_init(&write_semaphore, false, size));
	CHECK(pthread_mutex_init(&read_lock, NULL));
	CHECK(pthread_mutex_init(&write_lock, NULL));
}

template <typename T>
DataQueue<T>::~DataQueue()
{
	delete[] data_array;
	CHECK(sem_destroy(&read_semaphore));
	CHECK(sem_destroy(&write_semaphore));
	CHECK(pthread_mutex_destroy(&read_lock));
	CHECK(pthread_mutex_destroy(&write_lock));
}

template <typename T>
void DataQueue<T>::put(const T& data)
{
	sem_wait(&write_semaphore);
	pthread_mutex_lock(&write_lock);

	data_array[last] = data;
	last = (last + 1) % size;
	
	pthread_mutex_unlock(&write_lock);
	sem_post(&read_semaphore);
}

template <typename T>
const T DataQueue<T>::get()
{
	sem_wait(&read_semaphore);
	pthread_mutex_lock(&read_lock);
	
	T result = data_array[first];
	first = (first + 1) % size;
	
	pthread_mutex_unlock(&read_lock);
	sem_post(&write_semaphore);
	return result;
}

#endif // _DATA_QUEUE_

